<!DOCTYPE html>
<html>
<head>
<title>01第1章  模块化编程.MD</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">

<style>
/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

body {
	font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
	font-size: var(--vscode-markdown-font-size, 14px);
	padding: 0 26px;
	line-height: var(--vscode-markdown-line-height, 22px);
	word-wrap: break-word;
}

#code-csp-warning {
	position: fixed;
	top: 0;
	right: 0;
	color: white;
	margin: 16px;
	text-align: center;
	font-size: 12px;
	font-family: sans-serif;
	background-color:#444444;
	cursor: pointer;
	padding: 6px;
	box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}

#code-csp-warning:hover {
	text-decoration: none;
	background-color:#007acc;
	box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}

body.scrollBeyondLastLine {
	margin-bottom: calc(100vh - 22px);
}

body.showEditorSelection .code-line {
	position: relative;
}

body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
	content: "";
	display: block;
	position: absolute;
	top: 0;
	left: -12px;
	height: 100%;
}

body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
	left: -30px;
}

.vscode-light.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(0, 0, 0, 0.15);
}

.vscode-light.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(0, 0, 0, 0.40);
}

.vscode-light.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-dark.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 255, 255, 0.4);
}

.vscode-dark.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 255, 255, 0.60);
}

.vscode-dark.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-high-contrast.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 160, 0, 0.7);
}

.vscode-high-contrast.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 160, 0, 1);
}

.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

img {
	max-width: 100%;
	max-height: 100%;
}

a {
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

a:focus,
input:focus,
select:focus,
textarea:focus {
	outline: 1px solid -webkit-focus-ring-color;
	outline-offset: -1px;
}

hr {
	border: 0;
	height: 2px;
	border-bottom: 2px solid;
}

h1 {
	padding-bottom: 0.3em;
	line-height: 1.2;
	border-bottom-width: 1px;
	border-bottom-style: solid;
}

h1, h2, h3 {
	font-weight: normal;
}

table {
	border-collapse: collapse;
}

table > thead > tr > th {
	text-align: left;
	border-bottom: 1px solid;
}

table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
	padding: 5px 10px;
}

table > tbody > tr + tr > td {
	border-top: 1px solid;
}

blockquote {
	margin: 0 7px 0 5px;
	padding: 0 16px 0 10px;
	border-left-width: 5px;
	border-left-style: solid;
}

code {
	font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
	font-size: 1em;
	line-height: 1.357em;
}

body.wordWrap pre {
	white-space: pre-wrap;
}

pre:not(.hljs),
pre.hljs code > div {
	padding: 16px;
	border-radius: 3px;
	overflow: auto;
}

pre code {
	color: var(--vscode-editor-foreground);
	tab-size: 4;
}

/** Theming */

.vscode-light pre {
	background-color: rgba(220, 220, 220, 0.4);
}

.vscode-dark pre {
	background-color: rgba(10, 10, 10, 0.4);
}

.vscode-high-contrast pre {
	background-color: rgb(0, 0, 0);
}

.vscode-high-contrast h1 {
	border-color: rgb(0, 0, 0);
}

.vscode-light table > thead > tr > th {
	border-color: rgba(0, 0, 0, 0.69);
}

.vscode-dark table > thead > tr > th {
	border-color: rgba(255, 255, 255, 0.69);
}

.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
	border-color: rgba(0, 0, 0, 0.18);
}

.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
	border-color: rgba(255, 255, 255, 0.18);
}

</style>

<style>
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */

/* Tomorrow Comment */
.hljs-comment,
.hljs-quote {
	color: #8e908c;
}

/* Tomorrow Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
	color: #c82829;
}

/* Tomorrow Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
	color: #f5871f;
}

/* Tomorrow Yellow */
.hljs-attribute {
	color: #eab700;
}

/* Tomorrow Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
	color: #718c00;
}

/* Tomorrow Blue */
.hljs-title,
.hljs-section {
	color: #4271ae;
}

/* Tomorrow Purple */
.hljs-keyword,
.hljs-selector-tag {
	color: #8959a8;
}

.hljs {
	display: block;
	overflow-x: auto;
	color: #4d4d4c;
	padding: 0.5em;
}

.hljs-emphasis {
	font-style: italic;
}

.hljs-strong {
	font-weight: bold;
}
</style>

<style>
/*
 * Markdown PDF CSS
 */

 body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
	padding: 0 12px;
}

pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	border-radius: 3px;
	overflow-x: auto;
	white-space: pre-wrap;
	overflow-wrap: break-word;
}

pre:not(.hljs) {
	padding: 23px;
	line-height: 19px;
}

blockquote {
	background: rgba(127, 127, 127, 0.1);
	border-color: rgba(0, 122, 204, 0.5);
}

.emoji {
	height: 1.4em;
}

code {
	font-size: 14px;
	line-height: 19px;
}

/* for inline code */
:not(pre):not(.hljs) > code {
	color: #C9AE75; /* Change the old color so it seems less like an error */
	font-size: inherit;
}

/* Page Break : use <div class="page"/> to insert page break
-------------------------------------------------------- */
.page {
	page-break-after: always;
}

</style>

<script src="https://unpkg.com/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
  <script>
    mermaid.initialize({
      startOnLoad: true,
      theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast')
          ? 'dark'
          : 'default'
    });
  </script>
<h1 id="%E7%AC%AC1%E7%AB%A0-%E6%A8%A1%E5%9D%97%E5%8C%96%E7%BC%96%E7%A8%8B">第1章  模块化编程</h1>
<ul>
<li><input type="checkbox" id="checkbox0"><label for="checkbox0"></label><strong>学习目标</strong></li>
</ul>
<ol>
<li>理解什么是模块化编程及模块化编程的优势</li>
<li>了解模块化的演变</li>
<li>理解常见的模块化解决方式</li>
</ol>
<h2 id="%E6%A6%82%E8%BF%B0">概述</h2>
<ol>
<li>
<p>ECMAScript 6.0（以下简称 ES6）是 JavaScript 语言的一代标准，已经在 2015 年 6 月正式发布。</p>
</li>
<li>
<p>ES6入门教程：阮一峰   https://es6.ruanyifeng.com/</p>
<img src="01第1章  模块化编程.assets/ES6入门教程封面.jpg" alt="ES6入门教程封面" style="zoom:50%;" />
</li>
<li>
<p>Node.js简单的说，就是运行在服务端的 JavaScript。它对 ES6 的支持度更高。</p>
<ol>
<li>是一个基于Chrome JavaScript 运行时建立的一个平台。</li>
<li>是一个事件驱动I/O服务端JavaScript环境，基于Google的V8引擎，V8引擎执行Javascript的速度非常快，性能非常好。</li>
</ol>
</li>
<li>
<p>Node.js入门教程：http://nodejs.cn/learn/the-nodejs-fs-module</p>
</li>
<li>
<p>Node.js v14.8.0 文档：http://nodejs.cn/api/</p>
</li>
</ol>
<h2 id="%E5%88%9D%E8%AF%86%E6%A8%A1%E5%9D%97%E5%8C%96%E6%80%9D%E6%83%B3">初识模块化思想</h2>
<p>模块化就是将js文件按照功能分离，根据需求引入不同的文件中。</p>
<h3 id="%E6%A8%A1%E5%9D%97%E5%8C%96%E7%89%B9%E7%82%B9">模块化特点</h3>
<ol>
<li>生产效率高：
<ol>
<li>各业务模块间解耦：各个都是独立的模块，它们之间是没有依赖关系。</li>
<li>各模块可以复用：模块功能比较单一，可在多个项目中使用。</li>
<li>避免命名冲突，解决全局变量泛用现象。</li>
</ol>
</li>
<li>维护成本低
<ol>
<li>每个模块实际上也是一个完整的项目，可以进行单独编译，调试。</li>
<li>方便单个模块功能调试、升级。</li>
<li>每个团队负责不同的模块，提升开发，测试效率。</li>
</ol>
</li>
</ol>
<h3 id="es6-%E6%A8%A1%E5%9D%97">ES6 模块</h3>
<ol>
<li>
<p>ES6 模块的设计思想是尽量的<strong>静态化</strong>，使得**编译时(不是运行时)**就能确定模块的依赖关系，以及输入和输出的变量。</p>
</li>
<li>
<p>ES6 的模块自动采用严格模式。</p>
<ol>
<li>变量必须先声明，再使用</li>
<li>对象的属性名不能重复</li>
</ol>
</li>
<li>
<p>ES6 模块功能主要由两个命令构成：<code>export</code>和<code>import</code>。</p>
<p>1)<code>export</code>命令用于规定模块的对外接口；</p>
<p>2)<code>import</code>命令用于输入其他模块提供的功能。</p>
</li>
</ol>
<h2 id="es6-%E6%A8%A1%E5%9D%97%E4%BD%BF%E7%94%A8%E4%B8%BE%E4%BE%8B">ES6 模块使用举例</h2>
<h3 id="%E4%BE%8B1%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%87%BA%E5%AF%BC%E5%85%A5%E5%8F%98%E9%87%8F%E5%9C%A8%E9%A1%B5%E9%9D%A2%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%A8%A1%E5%9D%97">例1，模块导出导入变量，在页面中使用模块。</h3>
<ol>
<li>定义模块文件exp.js，用于向外部暴露信息；</li>
<li>定义模块文件imp.js，用于从exp.js导入信息；</li>
<li>在useimp.html文件中使用imp.js文件。（不是node imp.js，因为介绍的是ES6模块，用于前端）</li>
<li>代码展示：</li>
</ol>
<p>exp.js代码。注意暴露变量的不同方法。</p>
<pre class="hljs"><code><div><span class="hljs-comment">//export用于导出变量</span>
<span class="hljs-comment">// export var name='李四';</span>
<span class="hljs-comment">// export var age=18;</span>
<span class="hljs-comment">// export let obj = {</span>
<span class="hljs-comment">//     title: "温州旧事",</span>
<span class="hljs-comment">//     price: 42</span>
<span class="hljs-comment">// };</span>
<span class="hljs-keyword">var</span> name=<span class="hljs-string">'李四'</span>;
<span class="hljs-keyword">var</span> age=<span class="hljs-number">18</span>;
<span class="hljs-keyword">let</span> obj = {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"温州旧事"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">42</span>
};

<span class="hljs-keyword">export</span> {name,age,obj};
</div></code></pre>
<p>imp.js代码。注意import...from...命令的使用方法 。导入的变量不能修改，但对象的属性可以修改。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// import用于导入其它模块</span>
<span class="hljs-keyword">import</span> { name, age, obj } <span class="hljs-keyword">from</span> <span class="hljs-string">'./exp.js'</span>
<span class="hljs-comment">// 可以修改name和age的值吗？可以修改obj的属性吗？</span>
<span class="hljs-comment">// name = "王五"//不可以</span>
<span class="hljs-comment">// age=29;//不可以</span>
obj.price=<span class="hljs-number">55</span>;<span class="hljs-comment">//可以</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`你好，我是<span class="hljs-subst">${name}</span>，今年<span class="hljs-subst">${age}</span>了。`</span>)
</div></code></pre>
<p>useimp.html。注意在前端页面中使用模块的方法，特别是<code>type=&quot;module&quot;</code>。</p>
<pre class="hljs"><code><div><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- imp.js成为了单独的模块，而单独的模块有自己的作用域 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"imp.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</div></code></pre>
<p>通过&quot;Open with Live Server&quot;请求页面并检查（F12）浏览结果。需要说明的是，本操作需要在vscode中安装“Live Server&quot;插件。</p>
<pre class="hljs"><code><div>你好，我是李四，今年18了。
</div></code></pre>
<h3 id="%E4%BE%8B2%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%87%BA%E5%AF%BC%E5%85%A5%E5%87%BD%E6%95%B0">例2，模块导出导入函数</h3>
<ol>
<li>
<p>定义模块文件exp2.js，用于向外部暴露信息；</p>
</li>
<li>
<p>定义模块文件imp2.js，用于从exp.js导入信息；</p>
</li>
<li>
<p>在useimp2.html文件中使用imp.js文件。</p>
</li>
<li>
<p>代码展示：</p>
<p>exp2.js代码。注意别名的使用，原名将不能再用。</p>
</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-comment">//export用于导出函数</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fn1</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'fn1'</span>)
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fn2</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'fn2'</span>)
}
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addFn</span>(<span class="hljs-params">a,b</span>)</span>{<span class="hljs-keyword">return</span> a+b;}
<span class="hljs-keyword">export</span> {fn1 <span class="hljs-keyword">as</span> myFn1, fn2 <span class="hljs-keyword">as</span> myFn2}
</div></code></pre>
<p>imp2.js代码。注意静态导入的含义。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// import用于导入其它模块</span>
<span class="hljs-keyword">import</span> { myFn1, myFn2 } <span class="hljs-keyword">from</span> <span class="hljs-string">'./exp2.js'</span>
<span class="hljs-keyword">import</span> { addFn <span class="hljs-keyword">as</span> add } <span class="hljs-keyword">from</span> <span class="hljs-string">'./exp2.js'</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`10+20=<span class="hljs-subst">${add(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>)}</span>`</span>)
myFn1();
myFn2();


<span class="hljs-comment">// module是静态导入，不能使用需要运行后的结果，如</span>
<span class="hljs-comment">// let path="./exp2.js"</span>
<span class="hljs-comment">// import { addFn } from path  //静态导入，不能使用变量</span>
<span class="hljs-comment">// console.log(`${ addFn(10,20) }`)</span>

<span class="hljs-comment">// if(2&gt;1){//静态导入，不能待运行结果再导入</span>
<span class="hljs-comment">//     import { myFn1 } from "./exp2.js"</span>
<span class="hljs-comment">// }</span>
</div></code></pre>
<p>useimp2.html。浏览查看结果方法参考例1。</p>
<pre class="hljs"><code><div><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- imp.js成为了单独的模块，而单独的模块有自己的作用域 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"imp2.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</div></code></pre>
<h3 id="%E4%BE%8B3%E6%95%B4%E4%BD%93%E5%8A%A0%E8%BD%BD">例3，整体加载</h3>
<p>​	除了指定加载某个输出值，还可以使用整体加载，即用星号（*）指定一个对象，所有输出值都加载在这个对象上面。</p>
<ol>
<li>
<p>定义exp3.js文件，它输出两个方法<code>area</code>和<code>circumference</code>，分别表示圆求面积和求周长方法。</p>
</li>
<li>
<p>定义imp3.js文件，整体导入exp3.js，求圆面积，求圆周长。</p>
</li>
<li>
<p>在useimp3.html中使用imp3.js。</p>
</li>
<li>
<p>代码展示。</p>
<p>exp3.js。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">area</span>(<span class="hljs-params">radius</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.PI * radius * radius;
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">circumference</span>(<span class="hljs-params">radius</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI * radius;
}
</div></code></pre>
<p>imp3.js。注意import命令整体加载模块的方法。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> circle <span class="hljs-keyword">from</span> <span class="hljs-string">'./exp3.js'</span>;

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'圆面积：'</span> + <span class="hljs-built_in">Math</span>.round(circle.area(<span class="hljs-number">4</span>)*<span class="hljs-number">100</span>)/<span class="hljs-number">100</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'圆周长：'</span> + <span class="hljs-built_in">Math</span>.round(circle.circumference(<span class="hljs-number">14</span>)*<span class="hljs-number">100</span>)/<span class="hljs-number">100</span>);
</div></code></pre>
<p>useimp3.html。浏览查看结果方法参考例1。</p>
<pre class="hljs"><code><div>&lt;!DOCTYPE html&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"imp3.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
</div></code></pre>
</li>
</ol>
<h3 id="%E4%BE%8B4export-default%E7%9A%84%E4%BD%BF%E7%94%A8">例4，export default的使用</h3>
<p>​	为了给用户提供方便，让他们不用阅读文档就能加载模块，就要用到<code>export default</code>命令，为模块指定默认输出。</p>
<ol>
<li>
<p>定义exp4.js文件，它内部定义一个求两数相加的函数，并默认输出。</p>
</li>
<li>
<p>定义imp4.js文件，导入addexp4js，求圆面积，求圆周长。</p>
</li>
<li>
<p>在useimp4.html中使用imp4.js。</p>
</li>
<li>
<p>代码展示。</p>
<p>exp4.js文件。注意暴露add函数的写法，前面是<code>export default</code>。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">x,y</span>)</span>{
    <span class="hljs-keyword">return</span> x+y;
}
</div></code></pre>
<p>imp4.js文件。注意导入函数名的写法，它一般与导入的文件主名相同。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">import</span> add <span class="hljs-keyword">from</span> <span class="hljs-string">"./exp4.js"</span>;  <span class="hljs-comment">//导入名一般与exp4.js主文件名同名，但也可不同</span>
<span class="hljs-built_in">console</span>.log(add(<span class="hljs-number">14</span>,<span class="hljs-number">15</span>))
</div></code></pre>
<p>useimp4.html代码略。</p>
</li>
</ol>
<h2 id="nodejs%E6%A8%A1%E5%9D%97%E7%B3%BB%E7%BB%9F">Node.js模块系统</h2>
<p>一个 Node.js 文件就是一个模块，这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。</p>
<p>在执行模块代码之前，Node.js 会使用一个如下的函数封装器将其封装：</p>
<pre class="hljs"><code><div>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">exports, require, module, __filename, __dirname</span>) </span>{
<span class="hljs-comment">// 模块的代码实际上在这里</span>
});
</div></code></pre>
<p>所以，Node.js通过<code>require()</code>、<code>exports</code>、<code>module.exports</code>、<code>__filename</code>、 <code>__dirname</code> 来操作模块。</p>
<p>首先看一下<code>module</code>是什么。在d盘临时建立temp文件夹，在其中建立test.js文件，其文件内容如下所示，在vscode终端 node .\test.js执行，查看输出结果。</p>
<pre class="hljs"><code><div><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">module</span>)
</div></code></pre>
<p>​	输出结果如下所示。</p>
<pre class="hljs"><code><div>Module {
  <span class="hljs-attr">id</span>: <span class="hljs-string">'.'</span>,
  <span class="hljs-attr">exports</span>: {},
  <span class="hljs-attr">parent</span>: <span class="hljs-literal">null</span>,
  <span class="hljs-attr">filename</span>: <span class="hljs-string">'D:\\temp\\test.js'</span>,
  <span class="hljs-attr">loaded</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">children</span>: [],
  <span class="hljs-attr">paths</span>: [ <span class="hljs-string">'D:\\temp\\node_modules'</span>, <span class="hljs-string">'D:\\node_modules'</span> ] }
</div></code></pre>
<p>​	可以看出，<code>module</code>是对象，其中含有id、exports、parent、filename、loaded、children、paths等属性，这些属性的含义如下。</p>
<ul>
<li>module.id 表示模块的标识符。</li>
<li>module.exports 表示模块的导出对象。</li>
<li>module.parent 表示当前模块的父模块，当前模块是谁加载的。</li>
<li>module.filename 表示模块的绝对路径。</li>
<li>module.loaded 表示是否加载完成。</li>
<li>module.children 表示当前模块加载了哪些模块。</li>
<li>module.paths 表示模块的搜索路径，路径的多少取决于目录的深度。</li>
</ul>
<p>​	由上可知，<code>module.exports </code>对象是由模块系统创建的。在写模块的时候，需要在模块最后写好模块接口，<code>module.exports</code> 提供了暴露接口的方法。下面简单举例，使用<code>module.exports</code>。</p>
<p>​	新建data.js文件，文件内容如下所示，再用<strong>node data.js</strong>执行这个文件代码。</p>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> data=[
    {<span class="hljs-attr">name</span>:<span class="hljs-string">'张三'</span>,<span class="hljs-attr">age</span>:<span class="hljs-number">24</span>},
    {<span class="hljs-attr">name</span>:<span class="hljs-string">'李四'</span>,<span class="hljs-attr">age</span>:<span class="hljs-number">25</span>},
    {<span class="hljs-attr">name</span>:<span class="hljs-string">'王五'</span>,<span class="hljs-attr">age</span>:<span class="hljs-number">26</span>}
];
<span class="hljs-comment">//console.log(module.exports);   //{}</span>
<span class="hljs-built_in">module</span>.exports=data;
<span class="hljs-comment">//console.log(module.exports);     </span>
</div></code></pre>
<p>​	上述代码第6行，输出为空对象{}，可见<code>module.exports</code>是对象。经第7行赋值后，第8行输出如下所示。</p>
<pre class="hljs"><code><div>[ { <span class="hljs-attr">name</span>: <span class="hljs-string">'张三'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">24</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">'李四'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span> },
  { <span class="hljs-attr">name</span>: <span class="hljs-string">'王五'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">26</span> } ]
</div></code></pre>
<p>​	现定义use-data.js文件，使用data.js暴露给外部的数据。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// use-data.js</span>
<span class="hljs-keyword">var</span> data = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./data.js'</span>)<span class="hljs-comment">//  ./表示当前目录下</span>
<span class="hljs-built_in">console</span>.log(data)
</div></code></pre>
<p>​	在终端执行命令：</p>
<pre class="hljs"><code><div>node use-data.js
</div></code></pre>
<p>​	输出与前面相同。可见，在node.js中，可以在一个模块文件中用<code>module.exports</code>指定要暴露给外部的信息，而在应用模块文件中，可以用require()引入模块内容。</p>
<p>​	node.js 通过require方法加载文件是同步的，为了提高效率，会缓存已经加载过的文件。如果用下面语句输出<code>require</code>方法，可以发现<code>require</code>方法内部有四部分组成。</p>
<pre class="hljs"><code><div><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">require</span>);
</div></code></pre>
<ol>
<li>require.cache：被引入的模块将被缓存在这个对象中。</li>
<li>require.extensions：获取模块支持的扩展名。node.js中模块文件分三种：（1）.js模块 （ 2）.json文件模块 （ 3）.node C++扩展二进制模块。</li>
<li>require.main：获取入口模块。</li>
<li>require.resolve：获取模块的绝对路径，但并不会加载模块。</li>
</ol>
<h2 id="nodejs%E6%A8%A1%E5%9D%97%E5%88%86%E7%B1%BB">Node.js模块分类</h2>
<h3 id="%E4%B8%89%E7%B1%BB%E6%A8%A1%E5%9D%97">三类模块</h3>
<ol>
<li>核心模块，编译二进制，加载速度最快，像 fs，http，events等。
<ul>
<li>http：提供HTTP服务器功能。</li>
<li>url：解析URL。</li>
<li>fs：与文件系统交互。</li>
<li>querystring：解析URL的查询字符串。</li>
<li>child_process：新建子进程。</li>
<li>util：提供一系列实用小工具。</li>
<li>path：处理文件路径。</li>
<li>crypto：提供加密和解密功能，基本上是对OpenSSL的包装。</li>
</ul>
</li>
<li>文件模块，保存在硬盘上，加载速度比较慢，通过名称或路径来加载。</li>
<li>第三方模块，只指定名称则从node_modules目录下加载，查询的路径包括module.paths和全局目录。</li>
</ol>
<h3 id="nodejs%E6%A8%A1%E5%9D%97%E6%9F%A5%E6%89%BE%E8%A7%84%E5%88%99%E4%BA%86%E8%A7%A3%E4%B8%80%E4%B8%8B">Node.js模块查找规则（了解一下）</h3>
<ol>
<li>require方法根据模块路径査找模块，如果是完整路径，直接引入模块。</li>
</ol>
<pre class="hljs"><code><div> <span class="hljs-built_in">require</span>(<span class="hljs-string">'./find.js'</span>);
</div></code></pre>
<ol start="2">
<li>如果模块后缀省略</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-built_in">require</span>(<span class="hljs-string">'./find'</span>)
</div></code></pre>
<p>（1）先找同名JS文件再找同名JS文件夹；
（2）如果找到了同名文件夹,找文件夹中的 index.js；
（3）如果文件夹中没有 index js就会去当前文件夹中的 package. js文件中查找main选项中的入口文件；
（4）如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到；</p>
<ol start="3">
<li>当模块没有路径且没有后缀</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-built_in">require</span>(<span class="hljs-string">'find'</span>)
</div></code></pre>
<p>（1）Node.js会假设它是系统模块；</p>
<p>（2）Node.js会去node_modules文件夹中；</p>
<p>（3）首先看是否有该名字的文件夹；</p>
<p>（4）如果是文件夹看里面是否有index.js；</p>
<p>（5）如果没有Index.js，查看文件夹中的package.json中的main选项确定模块入口文件；</p>
<p>（6）否则找不到报错。</p>
<h2 id="nodejs%E6%A8%A1%E5%9D%97%E7%B3%BB%E7%BB%9F%E4%B8%BE%E4%BE%8B">Node.js模块系统举例</h2>
<h3 id="%E4%BE%8B1-%E6%A8%A1%E5%9D%97%E6%96%87%E4%BB%B6%E6%9A%B4%E9%9C%B2%E4%B8%80%E4%B8%AA%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0">例1 模块文件暴露一个构造函数。</h3>
<ol>
<li>定义person.js文件，它l输出Person构造函数。</li>
<li>定义use-person.js文件，引入person.js，定义两个Person对象，并输出。</li>
<li>代码展示。</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-comment">// person.js</span>
<span class="hljs-keyword">var</span> Person=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name,age</span>)</span>{
    <span class="hljs-keyword">this</span>.name=name;<span class="hljs-keyword">this</span>.age=age;
}
<span class="hljs-built_in">module</span>.exports=Person;
</div></code></pre>
<p>​	use-person.js文件代码。</p>
<pre class="hljs"><code><div><span class="hljs-comment">// use-person.js</span>
<span class="hljs-keyword">var</span> Person=<span class="hljs-built_in">require</span>(<span class="hljs-string">'./person.js'</span>);
<span class="hljs-keyword">var</span> p1=<span class="hljs-keyword">new</span> Person(<span class="hljs-string">'张三'</span>,<span class="hljs-number">20</span>)
<span class="hljs-built_in">console</span>.log(p1);

<span class="hljs-keyword">var</span> p2=<span class="hljs-keyword">new</span> Person(<span class="hljs-string">'李四'</span>,<span class="hljs-number">21</span>)
<span class="hljs-built_in">console</span>.log(p2);
</div></code></pre>
<p>​	在vscode终端执行如下命令。</p>
<pre class="hljs"><code><div>node use-person.js
</div></code></pre>
<p>​	执行结果：</p>
<pre class="hljs"><code><div>Person { <span class="hljs-attr">name</span>: <span class="hljs-string">'张三'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">20</span> }
Person { <span class="hljs-attr">name</span>: <span class="hljs-string">'李四'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">21</span> }
</div></code></pre>
<h3 id="%E4%BE%8B2-%E8%BF%94%E5%9B%9E%E4%B8%80%E4%B8%AAjson%E5%AF%B9%E8%B1%A1">例2 返回一个JSON对象</h3>
<ol>
<li>创建文件app-json.js，代码如下所示，其中定义了name和version属性，以及sayName方法。</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-comment">// app-json.js</span>
<span class="hljs-keyword">var</span> app = {
    <span class="hljs-attr">name</span>: <span class="hljs-string">'app'</span>,
    <span class="hljs-attr">version</span>: <span class="hljs-string">'1.0.0'</span>,
    <span class="hljs-attr">sayName</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
    }
}
<span class="hljs-built_in">module</span>.exports = app;
</div></code></pre>
<ol start="2">
<li>创建文件use-app-json.js，代码如下所示，</li>
</ol>
<pre class="hljs"><code><div><span class="hljs-keyword">var</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./app-json.js'</span>);<span class="hljs-comment">//可以去掉.js，但不能去掉./</span>
<span class="hljs-built_in">console</span>.log(app.name,app.version)
app.sayName();<span class="hljs-comment">//app</span>
</div></code></pre>
<p>核心模块，以及第三方模块在后续再作补充。</p>
<h2 id="%E5%B0%8F%E7%BB%93">小结</h2>
<p>本章介绍了模块化思想，举例介绍了ES6中的模块。本单还介绍了Node.js模块系统分类、模块查找规则等，还举例介绍了Node.js模块系统。Node.js模块化编程思想是后续编程基础，需要好好掌握。模块化查找规则在后续课时中需要进一步巩固。</p>

</body>
</html>
